#include "Tablero.h"

Tablero::Tablero() {
	this->cantidadColumnas=0;
	this->cantidadFilas=0;
	this->tablero=NULL;
	this->alturaDeFilaActual=0;
	this->alturaDeFilaAnterior=0;

}

void Tablero::setDimensiones(unsigned int dimensionX,unsigned int dimensionY){
	if ((dimensionX==0)||(dimensionY==0)) return;
	if (this->tablero!=NULL){
		for(unsigned int i=0;i<this->cantidadColumnas;i++){
			delete[] this->tablero[i];							//destruye el tablero anterior si se vuelve a llamar este metodo
		}
		delete[] this->tablero;
	}

	this->cantidadColumnas=dimensionX;
	this->cantidadFilas=dimensionY;
	this->tablero= new Casillero*[dimensionX];
	for(unsigned int i=0;i<dimensionX;i++){
		this->tablero[i]=new Casillero[dimensionY];
	}
}


void Tablero::setCasillero(Casillero c,unsigned int ubicacionX,unsigned int ubicacionY){
	this->tablero[ubicacionX][ubicacionY]=c;
}


Casillero Tablero::getCasillero(unsigned int ubicacionX,unsigned int ubicacionY){
	Casillero casilleroVacio;
	if ((this->tablero!=NULL)&&(ubicacionX<this->cantidadColumnas)&&(ubicacionY<this->cantidadFilas)){
		return (this->tablero[ubicacionX][ubicacionY]);
	} else return casilleroVacio;
}

bool Tablero::insPieza(Pieza *pieza,unsigned int ubicacionX,unsigned int ubicacionY){    //funcion auxiliar, devuelve true si pudo insertar la pieza, false si hubo una colision
	bool colision=false;
	//primero comprueba que no haya una colision en la zona donde se pondra la pieza
	for(unsigned int i=0;i<pieza->getCantidadColumnas();i++){
		for(unsigned int j=0;j<pieza->getCantidadFilas();j++){
			colision= colision || ((pieza->getCasillero(i,j).getEstado()) && (this->getCasillero(ubicacionX+i,ubicacionY+j).getEstado()));
		}
	}
	//si no hay una colision, "pega" la pieza en el tablero
	if(!colision){
		for(unsigned int i=0;i<pieza->getCantidadColumnas();i++){
			for(unsigned int j=0;j<pieza->getCantidadFilas();j++){
				if (pieza->getCasillero(i,j).getEstado()) {     //solo "pega" los casilleros llenos de la pieza, sino pegaria cualquier cosa
					this->setCasillero(pieza->getCasillero(i,j),ubicacionX+i,ubicacionY+j);
				}
			}
		}
		return true; //si se pudo insertar la pieza sin colision
	}
	return false; // si llega aca significa que hubo colision y se salto el if


}

bool Tablero::insertarPieza(Pieza *pieza){

	bool seLogroInsertar=false;
	unsigned int intentoDeInsercion=1;

	//No, porque se supone que la pieza va a estar limitada por sus filas y columnas, es decir que si o si va a tener
	//al menos un casillero "lleno" en los cuatro bordes
	if ((pieza==NULL)||(pieza->getCantidadColumnas()==0)||(pieza->getCantidadFilas()==0)) return false;

	while((!seLogroInsertar)&&(intentoDeInsercion<5)){
		for(int ubicacionY=0;(ubicacionY<=(this->cantidadFilas - pieza->getCantidadFilas()) && (!seLogroInsertar));ubicacionY++){
			for(int ubicacionX=0;(ubicacionX<=(this->cantidadColumnas - pieza->getCantidadColumnas()) && (!seLogroInsertar));ubicacionX++){
				if (!seLogroInsertar) seLogroInsertar=this->insPieza(pieza,ubicacionX,ubicacionY);
			}
		}
		pieza->rotar();
		intentoDeInsercion++;
	}

	return seLogroInsertar;

}

bool Tablero::insertarPiezaTP1(Pieza *pieza){

	bool piezaInsertada=false;
	unsigned int ubicacionY;
	if ((pieza==NULL)||(pieza->getCantidadColumnas()==0)||(pieza->getCantidadFilas()==0)) return false;


	ubicacionY=this->alturaDeFilaAnterior;	//se ubica en la fila "actual" para comenzar a ubicar la pieza
	if (ubicacionY+ pieza->getCantidadFilas()> this->cantidadFilas) return false; // si ya se excedio del rango, cancela la insercion
	for(unsigned int ubicacionX=0; (ubicacionX<=(this->cantidadColumnas - pieza->getCantidadColumnas()) && (!piezaInsertada));ubicacionX++){
		piezaInsertada=this->insPieza(pieza,ubicacionX,ubicacionY); // itera en la fila actual
	}

	if(!piezaInsertada){ //si todavia no se logro insertar la pieza, pasa a la fila siguiente a la actual
		ubicacionY=this->alturaDeFilaActual;
		if (ubicacionY+ pieza->getCantidadFilas()> this->cantidadFilas) return false; //nuevamente, comprueba que no se haya excedido de rango
		for(unsigned int ubicacionX=0; (ubicacionX<=(this->cantidadColumnas - pieza->getCantidadColumnas()) && (!piezaInsertada));ubicacionX++){
			piezaInsertada=this->insPieza(pieza,ubicacionX,ubicacionY); //itera en la fila SIGUIENTE a la actual
		}
	}

	// si se logro insertar la pieza en la fila actual y no en la siguiente, hay que comprobar el nuevo tamanio
	// de la fila actual, ya que la pieza insertada puede ser mas alta que la fila
	if (piezaInsertada && (ubicacionY==this->alturaDeFilaAnterior) && (this->alturaDeFilaActual < (pieza->getCantidadFilas() + ubicacionY)) ){
		this->alturaDeFilaActual= pieza->getCantidadFilas() + ubicacionY;
	}

	if (piezaInsertada && (ubicacionY==this->alturaDeFilaActual)){ // si se logro insertar la pieza pero hay que avanzar de fila, se hace lo siguiente:
		this->alturaDeFilaAnterior=this->alturaDeFilaActual;	//la fila actual ahora es la fila anterior
		this->alturaDeFilaActual=this->alturaDeFilaAnterior + pieza->getCantidadFilas();	//y ahora se debera intentar insertar en la fila siguiente
	}

	return piezaInsertada;
}

float Tablero::calcularNivelDeOcupacion(){

	float cantidadDeCasillerosLLenos=0;
	float cantidadDeCasillerosTotales=(this->cantidadColumnas * this->cantidadFilas);

	for(unsigned int x=0;x<this->cantidadColumnas;x++){
		for (unsigned int y=0;y<this->cantidadFilas;y++){
			if (this->getCasillero(x,y).getEstado()) cantidadDeCasillerosLLenos++;
		}
	}
	return (cantidadDeCasillerosLLenos / cantidadDeCasillerosTotales);
}

void Tablero::mostrarPorConsola(){
	if((this->cantidadColumnas==0)||(this->cantidadFilas==0)){
		cout << "Tablero vacio!!"<< endl;
		return;
	}
	for(unsigned int j=0;j<this->cantidadColumnas;j++){
		for(unsigned int i=0;i<this->cantidadColumnas;i++){
			if (this->tablero[i][j].getEstado()) {
				cout << "X";
			}
			else {
				cout << "_";
			}
		}
		cout << endl;
	}
}

unsigned int Tablero::getAltura(){
	return this->cantidadFilas;
}

unsigned int Tablero::getAncho(){
	return this->cantidadColumnas;
}

Tablero::~Tablero() {

	for(unsigned int i=0;i<this->cantidadColumnas;i++){
		delete[] this->tablero[i];
	}
	delete[] this->tablero;
}

